package moment

import (
	"database/sql/driver"
	"fmt"
	"strings"
	"time"

	"gitee.com/shoyer/logger"
	"gitee.com/shoyer/util"
)

const (
	FULL_FORMAT = "2006-01-02 15:04:05.000000000"
)

type Moment struct {
	time.Time
}

// 从当前时间构建时间对象
func NewMoment() *Moment {
	return &Moment{Time: time.Now()}
}

// 从时间 time.Time 构建对象
func NewMomentFromTime(t time.Time) *Moment {
	return &Moment{Time: t}
}

// 从时间指针 *time.Time 构建对象
func NewMomentFromTimePointer(t *time.Time) *Moment {
	return &Moment{Time: *t}
}

// 从标准日期【2006-01-02】构建对象
func NewMomentFromDate(date string) *Moment {
	t, err := time.Parse("2006-01-02", date)
	if err != nil {
		logger.Error(err)
		return nil
	}

	return &Moment{Time: t}
}

// 从标准时间【2006-01-02 15:04:05】构建对象
func NewMomentFromDateTime(datetime string) *Moment {
	t, err := time.Parse("2006-01-02 15:04:05", datetime)
	if err != nil {
		logger.Error(err)
		return nil
	}

	return &Moment{Time: t}
}

// 从任意格式构建对象, format参考: 2006-01-02 15:04:05
func NewMomentFromFormat(format, datetime string) *Moment {
	t, err := time.Parse(format, datetime)
	if err != nil {
		logger.Error(err)
		return nil
	}

	return &Moment{Time: t}
}

func NewMomentFromFormatInLocal(format, datetime string) *Moment {
	t, err := time.ParseInLocation(format, datetime, time.Local)
	if err != nil {
		logger.Error(err)
		return nil
	}

	return &Moment{Time: t}
}

// 从时间截构建对象
func NewMomentFromTimestamp(timestamp int64) *Moment {
	t := time.Unix(timestamp, 0)
	return &Moment{Time: t}
}

// 从毫秒时间截构建对象
func NewMomentFromMillisecond(millisecond int64) *Moment {
	// 得到秒以外的毫秒
	ms := millisecond % 1000

	// 得到纳秒
	nsec := ms * 1000000
	t := time.Unix(millisecond/1000, nsec)
	return &Moment{Time: t}
}

// func (m *Moment) String() string {
// 	return m.GetDateTime()
// }

func (m *Moment) UnmarshalJSON(data []byte) error {
	// 这里需要注意，需要去掉日期字符串前后带的双引号
	s := strings.ReplaceAll(string(data), `"`, ``)
	if strings.Contains(s, "T") {
		t := time.Time{}
		err := t.UnmarshalJSON(data)
		if err != nil {
			return err
		}
		*m = *NewMomentFromTime(t)
		return nil
	}

	if strings.Contains(s, "-") && strings.Contains(s, ":") && strings.Contains(s, ".") {
		*m = *NewMomentFromFormatInLocal("2006-01-02 15:04:05.999", s)
		return nil
	}

	if strings.Contains(s, "-") && strings.Contains(s, ":") {
		*m = *NewMomentFromFormatInLocal("2006-01-02 15:04:05", s)
		return nil
	}

	if strings.Contains(s, "-") {
		*m = *NewMomentFromFormatInLocal("2006-01-02", s)
		return nil
	}

	return fmt.Errorf("can not UnmarshalJSON time data to Moment: %s", string(data))
}

func (m *Moment) MarshalJSON() ([]byte, error) {
	// 要特别注意反引号，这里要么返回一个数字，要么返回一个字符串，字符串必须带双引号才能正确工作
	// https://cloud.tencent.com/developer/ask/sof/106769182
	return []byte(fmt.Sprintf(`"%s"`, m.Format("2006-01-02 15:04:05.999"))), nil
}

// 获得秒级时间截
func (m *Moment) GetTimestamp() int64 {
	return m.Unix()
}

// 获得毫秒时间截
func (m *Moment) GetMillisecond() int64 {
	return m.UnixNano() / 1000000
}

// 获得标准日期 "2006-01-02"
func (m *Moment) GetDate() string {
	return m.Format("2006-01-02")
}

// 获得标准日期时间 "2006-01-02 15:04:05"
func (m *Moment) GetDateTime() string {
	return m.Format("2006-01-02 15:04:05")
}

// 获得标准时间 "15:04:05"
func (m *Moment) GetTime() string {
	return m.Format("15:04:05")
}

// 获得utc iso标准时间
func (m *Moment) GetUtcISO() string {
	return m.UTC().Format("2006-01-02T15:04:05.999Z07:00")
}

func (m *Moment) GetDateTimeByUnderline() string {
	return m.Format("2006_01_02_15_04_05")
}

// 获得时间指针
func (m *Moment) TimePointer() *time.Time {
	return &m.Time
}

func (m *Moment) BeforeDuration(duration time.Duration) *Moment {
	d := m.GetMillisecond() - duration.Milliseconds()
	return NewMomentFromMillisecond(d)
}

// 当前时间向前x天
func (m *Moment) BeforeDays(days int) *Moment {
	d := m.GetMillisecond() - Day.Milliseconds()*int64(days)
	return NewMomentFromMillisecond(d)
}

// 当前时间向前x小时
func (m *Moment) BeforeHours(hours int) *Moment {
	d := m.GetMillisecond() - Hour.Milliseconds()*int64(hours)
	return NewMomentFromMillisecond(d)
}

// 当前时间向前x分钟
func (m *Moment) BeforeMinutes(minutes int) *Moment {
	d := m.GetMillisecond() - Minute.Milliseconds()*int64(minutes)
	return NewMomentFromMillisecond(d)
}

func (m *Moment) AfterDuration(duration time.Duration) *Moment {
	d := m.GetMillisecond() + duration.Milliseconds()
	return NewMomentFromMillisecond(d)
}

// 当前时间向前x天
func (m *Moment) AfterDays(days int) *Moment {
	d := m.GetMillisecond() + Day.Milliseconds()*int64(days)
	return NewMomentFromMillisecond(d)
}

// 当前时间向后x分钟
func (m *Moment) AfterMinutes(minutes int) *Moment {
	d := m.GetMillisecond() + Minute.Milliseconds()*int64(minutes)
	return NewMomentFromMillisecond(d)
}

// 当前时间向后x小时
func (m *Moment) AfterHours(hours int) *Moment {
	d := m.GetMillisecond() + Hour.Milliseconds()*int64(hours)
	return NewMomentFromMillisecond(d)
}

// 是否在mm之后
func (m *Moment) After(mm *Moment) bool {
	return m.Format(FULL_FORMAT) > mm.Format(FULL_FORMAT)
}

// 是否在mm之前
func (m *Moment) Before(mm *Moment) bool {
	return m.Format(FULL_FORMAT) < mm.Format(FULL_FORMAT)
}

// 是否相等
func (m *Moment) Equal(mm *Moment) bool {
	return m.Format(FULL_FORMAT) == mm.Format(FULL_FORMAT)
}

// 一天的开始时间点
func (m *Moment) StartOfDay() *Moment {
	return m.ToLocal().startOfDay()
}

// 一小时的开始时间点
func (m *Moment) StartOfHour() *Moment {
	return m.ToLocal().startOfHour()
}

func (m *Moment) StartOfMonth() *Moment {
	t := time.Date(m.Time.Year(), m.Time.Month(), 1, 0, 0, 0, 0, time.Local)
	return NewMomentFromTime(t).ToLocal()
}

func (m *Moment) EndOfMonth() *Moment {
	year := m.Time.Year()
	month := m.Time.Month() + 1
	if month > 12 {
		month = 1
		year += 1
	}

	t := time.Date(year, month, 1, 0, 0, 0, 0, time.Local).Add(-1 * time.Second)
	return NewMomentFromTime(t).ToLocal()
}

func (m *Moment) startOfDay() *Moment {
	ms := m.GetMillisecond() % Day.Milliseconds()
	diffSec := util.GetDiffSeconds()
	bs := m.GetMillisecond() - ms - diffSec*1000

	return NewMomentFromMillisecond(bs)
}

func (m *Moment) startOfHour() *Moment {
	ms := m.GetMillisecond() % Hour.Milliseconds()
	diffSec := util.GetDiffSeconds()
	bs := m.GetMillisecond() - ms - diffSec*1000

	return NewMomentFromMillisecond(bs)
}

func (m *Moment) ToLocal() *Moment {
	return NewMomentFromDateTime(m.GetDateTime())
}

func (m *Moment) ToUTC() *Moment {
	return NewMomentFromTime(m.UTC())
}

// 一天的结束时间点
func (m *Moment) EndOfDay() *Moment {
	return m.ToLocal().endOfDay()
}

func (m *Moment) StartOfNextDay() *Moment {
	return m.AfterDays(1).ToLocal().startOfDay()
}

func (m *Moment) EndOfHour() *Moment {
	return m.ToLocal().endOfHour()
}

func (m *Moment) endOfDay() *Moment {
	ms := m.startOfDay().GetMillisecond() + Day.Milliseconds() - 1000
	return NewMomentFromMillisecond(ms)
}

func (m *Moment) endOfHour() *Moment {
	ms := m.startOfHour().GetMillisecond() + Hour.Milliseconds() - 1000
	return NewMomentFromMillisecond(ms)
}

func (m *Moment) IsToday() bool {
	return m.StartOfDay().GetMillisecond() == NewMoment().StartOfDay().GetMillisecond()
}

func (m *Moment) IsYesterday() bool {
	return m.StartOfDay().GetMillisecond() == NewMoment().BeforeDays(1).StartOfDay().GetMillisecond()
}

// Scan implements the sql.Scanner interface for database deserialization.
func (m *Moment) Scan(value interface{}) error {
	if value == nil {
		return nil
	}

	m.Time = value.(time.Time)
	return nil
}

// Value implements the driver.Valuer interface for database serialization.
func (m Moment) Value() (driver.Value, error) {
	return *m.TimePointer(), nil
}

func (m *Moment) Add(t time.Duration) *Moment {
	return &Moment{Time: m.Time.Add(t)}
}
